1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.util.file;
12 import hip.util.conv:to;
13 import hip.util.array:join, array;
14 import hip.util.system;
15 import hip.util.path;
16 import hip.util.string;
17 
18 
19 string getFileContent(string path, bool noCarriageReturn = true)
20 {
21     import core.stdc.stdio;
22     path = sanitizePath(path);
23     FILE* file = fopen((path~"\0").ptr, "r");
24     if(!file)
25         return "";
26     char[] buffer;
27 
28     fseek(file, 0, SEEK_END);
29     auto size = ftell(file);
30     fseek(file, 0, SEEK_SET);
31 
32     buffer.length = cast(typeof(buffer.length))size;
33     size_t readSize = fread(buffer.ptr, cast(size_t)size, 1, file);
34     if(readSize != buffer.length)
35         buffer.length = readSize;
36     fclose(file);
37 
38     string content = cast(string)buffer;
39     return (noCarriageReturn) ? content.replaceAll('\r') : content;
40 }
41 
42 
43 
44 string stripLineBreaks(string content)
45 {
46     content = content.replaceAll('\r');
47     content = content.replaceAll('\n');
48     return content;
49 }
50 
51 // string getFileContentFromBasePath(string path, string basePath, bool noCarriageReturn = true)
52 // {
53 //     string finalPath = relativePath(sanitizePath(path), sanitizePath(basePath));
54 //     return getFileContent(finalPath, noCarriageReturn);
55 // }
56 
57 
58 
59 version(HipDStdFile)
60 {
61     import std.stdio:File;
62 
63     version(CustomRuntimeTest) version = CustomRuntime;
64     version(WebAssembly) version = CustomRuntime;
65     version(PSVita) version = CustomRuntime;
66 
67     version(CustomRuntimeTest) {void fileTruncate(File file, ptrdiff_t offset){}}
68     else
69     {
70         import std.file;
71         void fileTruncate(File file, ptrdiff_t offset) 
72         {
73             version (Windows) 
74             {
75                 import hip.util.windows;
76                 file.seek(offset);
77                 if(!SetEndOfFile(file.windowsHandle()))
78                     throw new FileException(file.name, "SetEndOfFile error");
79             }
80 
81             version (Posix) 
82             {
83                 import core.sys.posix.unistd: ftruncate;
84                 int res = ftruncate(file.fileno(), offset);
85                 if(res != 0)
86                     throw new FileException(file.name, "ftruncate error with code "~to!string(res));
87             }
88         }
89     }
90 }
91 
92 version(HipDStdFile) class FileProgression
93 {
94     protected ulong progress;
95     protected uint stepSize;
96     protected ulong fileSize;
97     protected void delegate(ref ubyte[] data) onFinish;
98     protected void delegate(float progress) onUpdate;
99     ubyte[] fileData;
100     ubyte[] buffer;
101     File target;
102 
103     /**
104     *   If bytes == 0, it will use readsteps.
105     *
106     *   Readsteps default is by progressing from 0 to 100, which makes great for percentage. Also notice that with 100 readsteps there's almost
107     *   no loss compared to reading the file in one go, so it is the recommended value.
108     *
109     *   The greater the readsteps, the more time it will take to read the file and the more precision the percentage will have. Usually 100 should be enough.
110     *
111     *   If the readsteps are greater than the filesize, it will clamp to fileSize as readsteps.
112     *   That means it will update at every byte
113     */
114     this(string filePath, uint readSteps = 100, uint bytes = 0)
115     {
116         assert(readSteps != 0 || bytes != 0, "Can't have readSteps and bytes both == 0");
117         progress = 0;
118         target = File(filePath, "r");
119         ulong fSize = fileSize = target.size;
120         assert(cast(uint)fSize < uint.max, "Filesize is greater than uint.max, contact the FileProgression mantainer");
121         fileData.reserve(cast(uint)fSize);
122         
123         if(readSteps > fSize)
124             readSteps = cast(uint)fSize;
125         if(bytes != 0)
126             readSteps = cast(uint)fSize/bytes;
127 
128         real sz =cast(real)fSize/readSteps;
129         if(sz != fSize/readSteps) //Odd
130         {
131             size_t remaining = cast(size_t)((sz-(fSize/readSteps))*readSteps);
132             buffer = new ubyte[remaining];
133             target.rawRead(buffer);
134             fileData~= buffer[];
135             progress+= remaining;
136             stepSize = cast(uint)(fSize-remaining)/readSteps;
137         }
138         else
139             stepSize = cast(uint)fSize/readSteps;
140         buffer.length = stepSize;
141     }
142 
143     void setOnFinish(void delegate(ref ubyte[] data) onFinish){this.onFinish = onFinish;}
144     void setOnUpdate(void delegate(float progress) onUpdate){this.onUpdate = onUpdate;}
145 
146     bool update()
147     {
148         target.rawRead(buffer);
149         fileData~= buffer[];
150         progress+=stepSize;
151         if(onUpdate)
152             onUpdate(getProgress());
153         bool finished = progress >= fileSize;
154         if(finished)
155         {
156             target.close();
157             if(onFinish)
158                 onFinish(this.fileData);
159         }
160 
161         return !finished;
162     }
163     
164 
165     float getProgress(){return progress/cast(float)fileSize;}
166     @property ulong readSize(){return fileData.length;}
167     @property ulong size(){return fileSize;}
168 
169 
170     override string toString(){return cast(string)fileData;}
171 }